﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SiteParser.Core.Utilites;
using System.IO;
using SiteParser.Core.Logging;
using System.Threading;
using System.Text.RegularExpressions;

namespace SiteParser.Core.ParserEntities
{
	public class PagingParser : ParserEntity
	{
		private int iterationCount;

		protected IList<string> parsedPagesUrls;
		protected Regex urlIdRegex;

		public int StartIndex { get; set; }
		public int MaximumIterationCount { get; set; }
		public string UrlIdRegexPattern { get; set; }

		public bool ReadCacheEnabled { get; set; }
		public bool WriteCacheEnabled { get; set; }
		public string EncodingName { get; set; }

		#region Private methods

		private bool NextIteration() {
			iterationCount++;
			if (MaximumIterationCount != 0) {
				return iterationCount <= MaximumIterationCount + StartIndex;
			}

			return true;
		}

		#endregion Private methods

		protected override void ProcessCurrentContext(ParsingContext context, IHtmlIterator htmlIterator) {
			Logger.Info("Processing current context for 'PagingParser'");

			urlIdRegex = new Regex(UrlIdRegexPattern);

			var contextObject = context.InnerEntity;

			var list = string.IsNullOrEmpty(PropertyName) ? contextObject :
				InitializeProperty(contextObject, PropertyName);

			var listTemplateParser = (ParserEntity)ChildEntities[0];

			bool notProcessedUrlsExists = true;

			string pageContent = HttpUrlResolver.GetReferenceStringContent(context.CurrentUrl, EncodingName, ReadCacheEnabled, WriteCacheEnabled);

			string notProcessedUrl = context.CurrentUrl;

			while (notProcessedUrlsExists && pageContent != null && NextIteration()) {
				ParsingContext pageContext = new ParsingContext() {
					CurrentUrl = notProcessedUrl,
					InnerEntity = list,
					InnerHtml = pageContent,
					ParentContext = context
				};

				if (iterationCount > StartIndex) {
					listTemplateParser.Parse(pageContext);
				}

				Logger.Info("Page is processed. Waiting 1000 ms before the next operation.");
				Thread.Sleep(1000);

				IHtmlIterator pagerIterator = GetPagerIterator(context, pageContent);
				MarkUrlProcessed(notProcessedUrl);
				notProcessedUrl = HttpUrlResolver.CombineUrls(context.CurrentUrl, GetNotProcessedUrl(context, pagerIterator));
				if (!string.IsNullOrEmpty(notProcessedUrl)) {
					pageContent = HttpUrlResolver.GetReferenceStringContent(notProcessedUrl, EncodingName, ReadCacheEnabled, WriteCacheEnabled);
				}
				else {
					notProcessedUrlsExists = false;
					if (parsedPagesUrls != null) {
						parsedPagesUrls.Clear();
					}
					Logger.Info("All pages are processed.");
				}
			}
		}

		protected virtual string GetNotProcessedUrl(ParsingContext context, IHtmlIterator htmlIterator) {
			if (parsedPagesUrls == null) {
				parsedPagesUrls = new List<string>();
			}

			if (!string.IsNullOrEmpty(GetUrlId(context.CurrentUrl)) && !parsedPagesUrls.Contains(GetUrlId(context.CurrentUrl))) {
				return context.CurrentUrl;
			}

			do {
				if (!string.IsNullOrEmpty(GetUrlId(htmlIterator.SelectedValue)) && 
					!parsedPagesUrls.Contains(GetUrlId(htmlIterator.SelectedValue))) {
					return htmlIterator.SelectedValue;
				}
			}
			while (htmlIterator.MoveNext());

			return null;
		}

		protected virtual string GetUrlId(string url) {
			return urlIdRegex.IsMatch(url) ? urlIdRegex.Match(url).Groups[1].Value : url;
		}

		protected virtual void MarkUrlProcessed(string url) {
			if (parsedPagesUrls == null) {
				parsedPagesUrls = new List<string>();
			}

			parsedPagesUrls.Add(GetUrlId(url));
		}

		protected virtual IHtmlIterator GetPagerIterator(ParsingContext context, string pageContent) {
			ParsingContext rootContext = context;

			Stack<HtmlNavigationExpression> pagerLocation = new Stack<HtmlNavigationExpression>();

			while (rootContext != null) {
				pagerLocation.Push(rootContext.CurrentExpression);
				rootContext = rootContext.ParentContext;
			}

			IHtmlIterator htmlIterator = null;
			while (pagerLocation.Count > 0) {
				HtmlNavigationExpression nodeExpression = pagerLocation.Pop();

				using (var contentReader = new StringReader(pageContent)) {
					var navigator = new HtmlNavigator(contentReader);
					htmlIterator = navigator.Select(nodeExpression);

					if (nodeExpression.TagMatchingRule != null) {
						for (int i = 0; i <= nodeExpression.TagMatchingRule.MatchIndex && htmlIterator.MoveNext(); i++) { }
					}

					pageContent = htmlIterator.SelectedValue;
				}
			}

			return htmlIterator;
		}
	}
}
